﻿/****************************************************************
  Author:  Ievgenii Nazaruk, ievgenii.nazaruk@gmail.com 
 License:  Microsoft Public License (Ms-PL)
          ( http://www.opensource.org/licenses/ms-pl.html or
            http://www.microsoft.com/opensource/licenses.mspx )
*****************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Documents;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Imaging;

using SpeakupBackend;
using SpeakupBackend.Themes;


namespace Frontend
{

    public enum ConversionType
    {
        Row,
        Column
    }

    public class GridToContentConversionException: Exception
    {
        public ConversionType CoversionType;
        public int Original;

        public GridToContentConversionException(ConversionType convType, int gridOriginal)
        {
            CoversionType = convType;
            Original = gridOriginal;
        }
    }
   
    public class TimeSheet: ContentControl
    {
        private DateTime m_dateBegin = DateTime.Now;
        public DateTime DateBegin
        {
            get
            {
                return m_dateBegin;
            }
            set
            {
                if (m_dateBegin != value)
                {
                    m_dateBegin = value;
                    UpdateTimeSheet();
                }
            }
        }

        private DateTime m_dateEnd = DateTime.Now + new TimeSpan(13,0,0,0);
        public DateTime DateEnd
        {
            get
            {
                return m_dateEnd;
            }
            set
            {
                if (m_dateEnd != value)
                {
                    m_dateEnd = value;
                    UpdateTimeSheet();
                }
            }
        }

        public void UpdateDate(DateTime begin, DateTime end)
        {
            m_dateBegin = begin;
            m_dateEnd = end;
            UpdateTimeSheet();
        }

        private TimeSpan m_timeBegin = new TimeSpan(8,0,0);
        public TimeSpan TimeBegin
        {
            get
            {
                return m_timeBegin;
            }
            set
            {
                if (m_timeBegin != value)
                {
                    m_timeBegin = value;
                    UpdateTimeSheet();
                }
            }
        }

        private TimeSpan m_timeEnd = new TimeSpan(22, 0, 0);
        public TimeSpan TimeEnd
        {
            get
            {
                return m_timeEnd;
            }
            set
            {
                if (m_timeEnd != value)
                {
                    m_timeEnd = value;
                    UpdateTimeSheet();
                }
            }
        }

        public void UpdateTime(TimeSpan begin, TimeSpan end)
        {
            m_timeBegin = begin;
            m_timeEnd = end;
            UpdateTimeSheet();
        }

        public TimeSpan TimeSlotDuration = new TimeSpan(0,30,0);

        private long DaySlotsCount
        {
            get
            {
                int count = (int)(DateEnd.Date - DateBegin.Date).TotalDays;
                count++;
                return count;
            }
        }
        private long TimeSlotsCount
        {
            get
            {
                TimeSpan delta = TimeEnd - TimeBegin;
                long count = delta.Ticks / TimeSlotDuration.Ticks;
                if (delta.Ticks % TimeSlotDuration.Ticks > 0)
                {
                    count++;
                }
                return count;                
            }
        }

        private TimeSlot GetTimeSlot(int i)
        {
            if(i < 0 || i >= TimeSlotsCount)
            {
                throw new IndexOutOfRangeException("TimeSlot index is incorrect: "+i);
            }

            TimeSpan begin = new TimeSpan(TimeBegin.Ticks + TimeSlotDuration.Ticks*i);
            TimeSpan end = new TimeSpan(begin.Ticks + TimeSlotDuration.Ticks);

            return new TimeSlot(begin,end);
        }
        private DateTime GetDaySlot(int i)
        {
            if (i < 0 || i >= DaySlotsCount)
            {
                throw new IndexOutOfRangeException("DaySlot index is incorrect: " + i);
            }

            return DateBegin.Date + new TimeSpan(i,0,0,0);
        }

        private int GetTimeSlotRow(TimeSlot timeSlot)
        {
            long ticks = timeSlot.Begin.Ticks - TimeBegin.Ticks;
            
            if (ticks < 0) return (int)TimeSlotsCount + 1;

            return (int)(ticks / TimeSlotDuration.Ticks) + 1;
        }
        private int GetTimeSlotRowSpan(TimeSlot timeSlot)
        {
            long span = timeSlot.Duration.Ticks / TimeSlotDuration.Ticks;

            if (span < 0) return 1;

            if (timeSlot.Duration.Ticks % TimeSlotDuration.Ticks > 0)
            {
                span++;
            }           

            return (int)span;
        }
        private int GetDaySlotColumn(DateTime daySlot)
        {
            TimeSpan delta = daySlot.Date - DateBegin.Date;

            int column = (int)delta.TotalDays + 1;

            if (column < 0) column = (int)DaySlotsCount + 1;

            return column;
        }

        /* Grid settings */
        private Grid Grid;

        private long ContentRowCount
        {
            get
            {
                return (Grid.RowDefinitions.Count - 1) / 2;
            }
        }
        private long ContentColumnCount
        {
            get
            {
                return (Grid.ColumnDefinitions.Count - 1) / 2;
            }
        }

        private Brush m_gridLineBrush = Brushes.Black.Clone();
        public Brush GridLineBrush 
        {
            get
            {
                return m_gridLineBrush;
            }
            set
            {
                m_gridLineBrush = value;
                SetupGrid();
            }
        }

        private uint m_gridLineWidth = 1;
        public uint GridLineWidth
        {
            get
            {
                return m_gridLineWidth;
            }
            set
            {
                m_gridLineWidth = value;
                SetupGrid();
            }
        }

        /* Regular cell settings */
        private int MinCellHeight = 15;
        private int MinCellWidth = 30;        

        /* Highlight headerName */
        private Brush HighlightBrush = Brushes.PeachPuff.Clone();
        private TextBlock RowHighlightHeader = new TextBlock();
        private DockPanel RowHighlightPanel = new DockPanel();
        private TextBlock ColumnHighlightHeader = new TextBlock();
        private DockPanel ColumnHighlightPanel = new DockPanel();

        /* Timesheet children */
        public List<UIElement> Children = new List<UIElement>(); 

        /* Timesheet week headerName */
        private Dictionary<int, Border> CurrentWeekLabels = new Dictionary<int, Border>();

        private Border TopLeftFiller = new Border();

        public TimeSheet()
        {            
            Grid.SetRow(TopLeftFiller, 1);
            Grid.SetRowSpan(TopLeftFiller, GridRowSpan(2));
            Grid.SetColumn(TopLeftFiller, 1);            
            TopLeftFiller.Background = Brushes.LightGray;

            this.BorderThickness = new Thickness(10);
            this.BorderBrush = Brushes.Red;

            Grid = new Grid();
            //Grid.ShowGridLines = true;

            Background = Brushes.LightCyan;
            SetupGrid();
            SetupHeaders();            

            Content = Grid;            
        }

        /* Grid setup */
        private void SetupGrid()
        {
            Grid.RowDefinitions.Clear();
            Grid.ColumnDefinitions.Clear();
            SetupGridRows();
            SetupGridColumns();
            SetupWeekHeaders();
        }

        private void SetupGridRows()
        {
            Grid.RowDefinitions.Clear();

            /* total row count: number of time slots + headerName + splitter for each row*/
            long rowCount = (TimeSlotsCount + 2) * 2 + 1 ;

            for (int i = 0; i < rowCount; i++)
            {
                RowDefinition rowdef = new RowDefinition();

                if (i % 2 == 0)
                {
                    rowdef.Height = GridLength.Auto;

                    Border filler = new Border();
                    filler.MinHeight = 1;
                    filler.Background = Brushes.Black;

                    Grid.Children.Add(filler);
                    Grid.SetZIndex(filler, -1);
                    Grid.SetRow(filler, i);
                    Grid.SetColumn(filler, 0);
                    Grid.SetColumnSpan(filler, Int32.MaxValue);
                }
                else
                {
                    if (i == 1 || i == 3)
                    {
                        rowdef.Height = GridLength.Auto;
                    }
                    else
                    {
                        rowdef.Height = new GridLength(1, GridUnitType.Star);
                        rowdef.MinHeight = MinCellHeight;
                    }
                }

                Grid.RowDefinitions.Add(rowdef);
            }

            
        }
        private void SetupGridColumns()
        {
            Grid.ColumnDefinitions.Clear();

            /* total column count: number of day slots + headerName + splitter for each column*/
            long colCount = (DaySlotsCount + 1) * 2 + 1;

            for (int i = 0; i < colCount; i++)            
            {
                ColumnDefinition coldef = new ColumnDefinition();

                if (i % 2 == 0)
                {
                    coldef.Width = GridLength.Auto;

                    Border filler = new Border();
                    filler.MinWidth = 1;
                    filler.Background = Brushes.Black;

                    Grid.SetZIndex(filler, -1);
                    Grid.SetColumn(filler, i);
                    Grid.SetRow(filler, 0);
                    Grid.SetRowSpan(filler, Int32.MaxValue);
                    Grid.Children.Add(filler);
                }
                else
                {
                    if (i == 1)
                    {
                        coldef.Width = GridLength.Auto;
                    }
                    else
                    {
                        coldef.Width = new GridLength(1, GridUnitType.Star);
                        coldef.MinWidth = MinCellWidth;
                    }
                }

                Grid.ColumnDefinitions.Add(coldef);
            }           
            
        }

        /* Grid row/column conversion: content (logical) rows to real grid rows */
        private int ContentRow(int gridRow)
        {
            if(gridRow % 2 != 0)
            {
                throw new GridToContentConversionException(ConversionType.Row, gridRow);
            }

            int row = (gridRow - 1)/2;
            return row;
        }
        private int ContentColumn(int gridColumn)
        {
            if (gridColumn % 2 != 0)
            {
                throw new GridToContentConversionException(ConversionType.Column, gridColumn);
            }

            int row = (gridColumn - 1) / 2;
            return row;
        }

        private int GridRow(int contentRow)
        {
            return contentRow * 2 + 1+2;
        }
        private int GridRowSpan(int rowSpan)
        {
            return rowSpan * 2 - 1;
        }
        private int GridColumn(int contentColumn)
        {
            return contentColumn * 2 + 1;
        }
        private int GridColumnSpan(int columnSpan)
        {
            return columnSpan * 2 + 1;
        }
        

        /* Headers setup */
        private void SetupHeaders()
        {
            SetupRowHeaders();   
            SetupColumnHeaders();
        }

        private String GetTimeSlotTitle(TimeSlot slot)
        {
            return slot.ToString();    
        }
        private String GetDaySlotTitle(DateTime slot)
        {
            DateTime day = slot.Date;

            return day.ToString("ddd, MMM dd", CultureInfo.InvariantCulture);
        }

        private void SetupRowHeaders()
        {
            if (!Grid.Children.Contains(TopLeftFiller))
            {
                Grid.Children.Add(TopLeftFiller);
            }

            RowHighlightHeader.VerticalAlignment = VerticalAlignment.Center;
            RowHighlightHeader.HorizontalAlignment = HorizontalAlignment.Center;
            RowHighlightHeader.Padding = new Thickness(1, 0, 1, 0);

            RowHighlightPanel.Children.Clear();
            RowHighlightPanel.Children.Add(RowHighlightHeader);
            RowHighlightPanel.Background = HighlightBrush;            
            Grid.SetZIndex(RowHighlightPanel, 100);
            
            for(int i = 0; i < TimeSlotsCount; i++)
            {
                TimeSlot slot = GetTimeSlot(i);
                String slotTitle = GetTimeSlotTitle(slot);

                TextBlock block = new TextBlock();
                block.Inlines.Add(new Run(slotTitle));
                block.VerticalAlignment = VerticalAlignment.Center;
                block.HorizontalAlignment = HorizontalAlignment.Center;
                block.Padding = new Thickness(1,0,1,0);

                Grid.SetRow(block, GridRow(i + 1));
                Grid.SetColumn(block, GridColumn(0));
                Grid.SetZIndex(block, -1);
                Grid.Children.Add(block);
            }
        }
        private void SetupColumnHeaders()
        {
            ColumnHighlightHeader.Background = HighlightBrush;
            ColumnHighlightHeader.VerticalAlignment = VerticalAlignment.Center;
            ColumnHighlightHeader.HorizontalAlignment = HorizontalAlignment.Center;
            ColumnHighlightHeader.Padding = new Thickness(1);
            ColumnHighlightHeader.TextWrapping = TextWrapping.WrapWithOverflow;

            ColumnHighlightPanel.Children.Clear();
            ColumnHighlightPanel.Children.Add(ColumnHighlightHeader);
            ColumnHighlightPanel.Background = HighlightBrush;
            Grid.SetZIndex(ColumnHighlightPanel, 100);            

            for (int i = 0; i < DaySlotsCount; i++)
            {
                DateTime slot = GetDaySlot(i);
                String slotTitle = GetDaySlotTitle(slot);

                TextBlock block = new TextBlock();
                block.Inlines.Add(new Run(slotTitle));
                block.VerticalAlignment = VerticalAlignment.Center;
                block.HorizontalAlignment = HorizontalAlignment.Center;
                block.Padding = new Thickness(1);
                block.TextWrapping = TextWrapping.WrapWithOverflow;
                
                Grid.SetColumn(block, GridColumn(i + 1));
                Grid.SetRow(block, GridRow(0));
                Grid.SetZIndex(block, -1);
                Grid.Children.Add(block);
            }         
        }

        private Border CreateWeekElement(string text)
        {
            TextBlock weekCtrl = new TextBlock();
            weekCtrl.TextAlignment = TextAlignment.Center;
            weekCtrl.VerticalAlignment = VerticalAlignment.Center;
            weekCtrl.HorizontalAlignment = HorizontalAlignment.Center;
            weekCtrl.TextWrapping = TextWrapping.Wrap;            
            weekCtrl.Text = text;
            weekCtrl.Padding = new Thickness(1);

            Border border = new Border();
            border.Child = weekCtrl;
            border.Background = Brushes.White;
            border.BorderBrush = Brushes.Black;
            border.BorderThickness = new Thickness(0, 2, 0, 0);
            return border;
        }

        private void SetupWeekHeaders()
        {
            int row = 1;
            
            foreach (var label in CurrentWeekLabels)
            {
                Grid.Children.Remove(label.Value);
            }
            CurrentWeekLabels.Clear();

            Grid.RowDefinitions[4].Height = new GridLength(2);

            DateTime weekDateBegin = DateBegin;
            DateTime weekDateEnd = weekDateBegin;

            //calculate
            while (true)
            {
                while (weekDateEnd.DayOfWeek != DayOfWeek.Sunday &&
                       weekDateEnd != DateEnd)
                {
                    weekDateEnd = weekDateEnd + TimeSpan.FromDays(1);
                }


                int beginColumn = GridColumn( GetDaySlotColumn(weekDateBegin) );
                int endColumn = GridColumn( GetDaySlotColumn(weekDateEnd) );
                int spanColumns = endColumn - beginColumn + 1;
                GridLength borderWidth = new GridLength(2);

                if (beginColumn > 0 && beginColumn < Grid.ColumnDefinitions.Count)
                {
                    Grid.ColumnDefinitions[beginColumn - 1].Width = borderWidth;
                }

                if (endColumn + 1 < Grid.ColumnDefinitions.Count)
                {
                    Grid.ColumnDefinitions[endColumn + 1].Width = borderWidth;
                }

                int week = Week.GetWeek(Week.KnownDate, Week.KnownWeek, weekDateBegin).WeekNumber;
                string title = "Week " + week.ToString();
                Border titleElement = CreateWeekElement(title);
               

                Grid.SetRow(titleElement, row);
                Grid.SetColumn(titleElement, beginColumn);
                Grid.SetColumnSpan(titleElement, spanColumns);
                this.Grid.Children.Add(titleElement);
                CurrentWeekLabels[week] = titleElement;

                if (weekDateEnd >= DateEnd) break;

                weekDateBegin = weekDateEnd + TimeSpan.FromDays(1);
                weekDateEnd = weekDateBegin;
            }            
        }

        private void EnableHeadersHighlight(UIElement element)
        {
            if (Grid.Children.Contains(element) == false)
            {
                return;
            }

            RowHighlightHeader.Inlines.Clear();
            ColumnHighlightHeader.Inlines.Clear();

            TimeSlot timeSlot = TimeSheet.GetTimeSlot(element);
            DateTime daySlot = TimeSheet.GetDaySlot(element);

            int row = Grid.GetRow(element);
            int rowSpan = Grid.GetRowSpan(element);
            int column = Grid.GetColumn(element);

            string rowTitle = GetTimeSlotTitle(timeSlot);
            string columnTitle = GetDaySlotTitle(daySlot);

            RowHighlightHeader.Inlines.Add(new Run(rowTitle));
            ColumnHighlightHeader.Inlines.Add(new Run(columnTitle));

            Grid.SetColumn(RowHighlightPanel, 1);
            Grid.SetRow(RowHighlightPanel, row);
            Grid.SetRowSpan(RowHighlightPanel, rowSpan);

            Grid.SetColumn(ColumnHighlightPanel, column);
            Grid.SetRow(ColumnHighlightPanel, 3);

            if (!Grid.Children.Contains(RowHighlightPanel))
            {
                Grid.Children.Add(RowHighlightPanel);
            }

            if (!Grid.Children.Contains(ColumnHighlightPanel))
            {
                Grid.Children.Add(ColumnHighlightPanel);
            }

            int week = Week.GetWeek(daySlot).WeekNumber;
            if (CurrentWeekLabels.Keys.Contains(week))
            {
                Border dec = CurrentWeekLabels[week];
                dec.Background = HighlightBrush;
            }
        }

        private void DisableHeadersHighlight()
        {
            if (Grid.Children.Contains(RowHighlightPanel))
            {
                Grid.Children.Remove(RowHighlightPanel);
            }

            if (Grid.Children.Contains(ColumnHighlightPanel))
            {
                Grid.Children.Remove(ColumnHighlightPanel);
            }

            foreach (Border ui in CurrentWeekLabels.Values)
            {
                ui.Background = Brushes.White;
            }            
        }

        void UpdateTimeSheet()
        {
            Grid.Children.Clear();
            SetupGrid();
            SetupHeaders();
            SetupWeekHeaders();

            foreach (UIElement element in Children)
            {
                SetElementDaySlot(element);
                SetElementTimeSlot(element);
                OnGridLocationChanged(element, null);
            }
        }

        /* TimeSheet children */

        static public readonly DependencyProperty TimeSlotProperty;
        static public readonly DependencyProperty DaySlotProperty;
        static private readonly DependencyProperty TimeSheetProperty;

        static TimeSheet()
        {
            FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
            metadata.DefaultValue = new TimeSlot(new TimeSpan(), new TimeSpan());
            metadata.Inherits = false;
            metadata.AffectsParentArrange = true;
            metadata.AffectsParentMeasure = true;
            metadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

            TimeSlotProperty = DependencyProperty.Register("TimeSlot",typeof(TimeSlot), typeof(TimeSheet), metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.DefaultValue = new DateTime(0);
            metadata.Inherits = false;
            metadata.AffectsParentArrange = true;
            metadata.AffectsParentMeasure = true;
            metadata.DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

            DaySlotProperty = DependencyProperty.Register("DaySlot", typeof(DateTime), typeof(TimeSheet), metadata);

            metadata = new FrameworkPropertyMetadata();
            metadata.DefaultValue = null;
            metadata.Inherits = false;

            TimeSheetProperty = DependencyProperty.Register("TimeSheet", typeof(TimeSheet), typeof(TimeSheet), metadata);
        }

        private static void SetTimeSheet(UIElement element, TimeSheet sheet)
        {
            element.SetValue(TimeSheetProperty, sheet);
        }
        public static void SetTimeSlot(UIElement element, TimeSlot slot)
        {
            element.SetValue(TimeSlotProperty, slot);
        }
        public static void SetDaySlot(UIElement element, DateTime slot)
        {
            element.SetValue(DaySlotProperty, slot);
        }

        private static TimeSheet GetTimeSheet(UIElement element)
        {
            return (TimeSheet)element.GetValue(TimeSheetProperty);
        }
        public static TimeSlot GetTimeSlot(UIElement element)
        {
            return (TimeSlot)element.GetValue(TimeSlotProperty);
        }
        public static DateTime GetDaySlot(UIElement element)
        {
            return (DateTime)element.GetValue(DaySlotProperty);
        }

        private void SetElementTimeSlot(UIElement element)
        {
            TimeSlot timeSlot = TimeSheet.GetTimeSlot(element);

            int row = GetTimeSlotRow(timeSlot);
            int rowSpan = GetTimeSlotRowSpan(timeSlot);

            if (timeSlot == null || rowSpan == 0)
            {
                Grid.SetRow(element, Int32.MaxValue);
            }
            else
            {
                Grid.SetRow(element, GridRow(row));
                Grid.SetRowSpan(element, GridRowSpan(rowSpan));
            }

        }
        private void SetElementDaySlot(UIElement element)
        {
            DateTime daySlot = TimeSheet.GetDaySlot(element);
            if (daySlot == null)
            {
                Grid.SetColumn(element, Int32.MaxValue);
            }
            else
            {
                int column = GetDaySlotColumn(daySlot);
                Grid.SetColumn(element, GridColumn(column));
            }

        }

        private static void OnTimeSlotPropertyChanged(object sender, EventArgs args)
        {
            UIElement element = (UIElement)sender;
            TimeSlot slot = TimeSheet.GetTimeSlot(element);
            TimeSheet timeSheet = TimeSheet.GetTimeSheet(element);

            timeSheet.SetElementTimeSlot(element);
        }
        private static void OnDaySlotPropertyChanged(object sender, EventArgs args)
        {
            UIElement element = (UIElement)sender;
            DateTime slot = TimeSheet.GetDaySlot(element);
            TimeSheet timeSheet = TimeSheet.GetTimeSheet(element);

            timeSheet.SetElementDaySlot(element);
        }
        private static void OnGridLocationChanged(object sender, EventArgs args)
        {
            UIElement element = (UIElement)sender;

            TimeSheet timeSheet = TimeSheet.GetTimeSheet(element);
            int column = Grid.GetColumn(element);
            int row = Grid.GetRow(element);

            if (column <= 1 ||
                row <= 1 ||
                column >= timeSheet.Grid.ColumnDefinitions.Count ||
                row >= timeSheet.Grid.RowDefinitions.Count)
            {
                if (timeSheet.Grid.Children.Contains(element))
                {
                    timeSheet.Grid.Children.Remove(element);
                }
            }
            else
            {
                if (timeSheet.Grid.Children.Contains(element) == false)
                {
                    timeSheet.Grid.Children.Add(element);
                }
            }
        }
        
        private void OnElementMouseEnter(object sender, MouseEventArgs e)
        {
            UIElement element = (UIElement)sender;            
            EnableHeadersHighlight(element);
        }

        private void OnElementMouseLeave(object sender, MouseEventArgs e)
        {
            UIElement element = (UIElement)sender;            
            
            DisableHeadersHighlight();

        }

        public void AddChildren(UIElement element)
        {
            TimeSheet.SetTimeSheet(element, this);

            DependencyPropertyDescriptor timeSlotDesc= 
                    DependencyPropertyDescriptor.FromProperty(TimeSheet.TimeSlotProperty, typeof(TimeSheet));

            timeSlotDesc.AddValueChanged(element, OnTimeSlotPropertyChanged);

            DependencyPropertyDescriptor daySlotDesc =
                    DependencyPropertyDescriptor.FromProperty(TimeSheet.DaySlotProperty, typeof(TimeSheet));

            daySlotDesc.AddValueChanged(element, OnDaySlotPropertyChanged);

            DependencyPropertyDescriptor gridColumnDesc =
                    DependencyPropertyDescriptor.FromProperty(Grid.ColumnProperty, typeof(Grid));

            gridColumnDesc.AddValueChanged(element, OnGridLocationChanged);

            DependencyPropertyDescriptor gridRowDesc =
                    DependencyPropertyDescriptor.FromProperty(Grid.RowProperty, typeof(Grid));

            gridRowDesc.AddValueChanged(element, OnGridLocationChanged);

            element.MouseEnter += OnElementMouseEnter;
            element.MouseLeave += OnElementMouseLeave;
            
            SetElementTimeSlot(element);
            SetElementDaySlot(element);
         
            Children.Add(element);
        }       
        public void RemoveChildren(UIElement element)
        {
            Children.Remove(element);
            Grid.Children.Remove(element);

            element.MouseEnter -= OnElementMouseEnter;
            element.MouseLeave -= OnElementMouseLeave;

            DependencyPropertyDescriptor descriptor =
                    DependencyPropertyDescriptor.FromProperty(TimeSheet.TimeSlotProperty, typeof(TimeSheet));
            
            descriptor.RemoveValueChanged(element, OnTimeSlotPropertyChanged);

            DependencyPropertyDescriptor daySlotDesc =
                    DependencyPropertyDescriptor.FromProperty(TimeSheet.DaySlotProperty, typeof(TimeSheet));

            daySlotDesc.RemoveValueChanged(element, OnDaySlotPropertyChanged);

            DependencyPropertyDescriptor gridColumnDesc =
                    DependencyPropertyDescriptor.FromProperty(Grid.ColumnProperty, typeof(Grid));

            gridColumnDesc.RemoveValueChanged(element, OnGridLocationChanged);

            DependencyPropertyDescriptor gridRowDesc =
                    DependencyPropertyDescriptor.FromProperty(Grid.RowProperty, typeof(Grid));

            gridRowDesc.RemoveValueChanged(element, OnGridLocationChanged);

            element.ClearValue(TimeSheetProperty);
        }


    }
}
